package main;

import java.util.Random;
import java.util.ArrayList;
import equipment.*;
import main.*;

public class Unit extends Army {
	public static UI myUI;
	public static Equipment[] equipChoices = {};
	
	// Game attributes
	public int strength;
	public int defense;
	public int attacks;
	public int wounds;
	public int courage;
	public int fightValue;
	public int shootValue;
	
	public int might = 0;
	public int will = 0;
	public int fate = 0;
	
	public double move;
	public double moveLeft;
	public int shootRange = 0;
	public int shootStrength = 2;
	public double moveNeededToShoot = 0.5;
	public boolean hasShot = false;
	
	
	// Individual attributes
	public int id;
	public String name;
	public int x;
	public int y;
	public int army;
	public ArrayList<Equipment> equipmentList = new ArrayList<Equipment>();
	
	// Battle attributes	
	public Battle battle = null;
	public int battleDice;
	public int battleBonus;
	public int woundDice;
	public int woundBonus;
	public boolean hasRetreated = true;
	
	public Unit supporting = null;
	public ArrayList<Unit> supporters = new ArrayList<Unit>();
	
	public static Random rng = new Random();
	
	public Unit() {
	}
	
	public void equip(Equipment e) {
		e.equipTo(this);
		this.equipmentList.add(e);
	}
	
	public boolean hasEquipment(Equipment e) {
		for (Equipment myE : equipmentList) {
			if (myE.getClass() == e.getClass())
				return true;
		}
		return false;
	}
	
	public void attackMove(int destX, int destY) {
		if (this.moveLeft == 0)
			return;
		if (destX < 0 || destX > myUI.game.board.pixelWidth 
				|| destY < 0 || destY > myUI.game.board.pixelHeight)
			return;
		if (this.supporting != null) {
			this.supporting.supporters.remove(this);
			this.supporting = null;
		}
		int rootX = this.x;
		int rootY = this.y;
		int newX = destX;
		int newY = destY;
		double shortDist = Utils.inchDist(rootX, rootY, newX, newY);
		Unit closeUnit = null;
		
		for(Army a : Game.armies){
			for(Unit u : a.units) {
				if (u.id != this.id){
					double d = Utils.pixelDist(rootX, rootY, u.x, u.y);
					double theta = Math.abs(Math.atan2(u.y-rootY, u.x-rootX) -
									Math.atan2(destY-rootY, destX-rootX));
					if (theta > Math.PI/2)
						continue;
					
					double ratio = Math.sin(theta)/30;
					double beta = Math.asin(ratio*d);
					double beta2 = Math.PI - beta;
					
					double gamma = Math.PI - (theta + beta);
					double gamma2 = Math.PI - (theta + beta2);
					double x1 = (Math.sin(gamma)/ratio)/30;
					double x2 = (Math.sin(gamma2)/ratio)/30;
					
					double x = Math.min(x1, x2);
					if (x < shortDist) {
						newX = (int)(rootX + x*30*(destX-rootX)/Utils.pixelDist(rootX, rootY, destX, destY));
						newY = (int)(rootY + x*30*(destY-rootY)/Utils.pixelDist(rootX, rootY, destX, destY));
						shortDist = x;
						closeUnit = u;
					}
				}
			}
		}
		
		if (closeUnit != null && Utils.inchDist(rootX, rootY, newX, newY) <= this.moveLeft) {
			if (!this.canContact(closeUnit)) {
				newX = rootX;
				newY = rootY;
			}
			else {
				this.moveLeft -= Utils.inchDist(rootX, rootY, newX, newY);
				this.contact(closeUnit);
				this.x = newX;
				this.y = newY;
			}
		}
		else if (Utils.inchDist(rootX, rootY, newX, newY) <= this.moveLeft){
			this.x = newX;
			this.y = newY;
			this.moveLeft -= Utils.inchDist(rootX, rootY, newX, newY);
		}
	}
	
	public void retreatMove(int destX, int destY) {
		if (this.moveLeft == 0)
			return;
		if (destX < 0 || destX > myUI.game.board.pixelWidth 
				|| destY < 0 || destY > myUI.game.board.pixelHeight)
			return;
		int rootX = this.x;
		int rootY = this.y;
		int newX = destX;
		int newY = destY;
		double shortDist = Utils.inchDist(rootX, rootY, newX, newY);
		Unit closeUnit = null;
		
		for(Army a : Game.armies){
			for(Unit u : a.units) {
				if (u.id != this.id){
					double d = Utils.pixelDist(rootX, rootY, u.x, u.y);
					double theta = Math.abs(Math.atan2(u.y-rootY, u.x-rootX) -
									Math.atan2(destY-rootY, destX-rootX));
					if (theta > Math.PI/2)
						continue;
					
					double ratio = Math.sin(theta)/30;
					double beta = Math.asin(ratio*d);
					double beta2 = Math.PI - beta;
					
					double gamma = Math.PI - (theta + beta);
					double gamma2 = Math.PI - (theta + beta2);
					double x1 = (Math.sin(gamma)/ratio)/30;
					double x2 = (Math.sin(gamma2)/ratio)/30;
					
					double x = Math.min(x1, x2);
					if (x < shortDist) {
						newX = (int)(rootX + x*30*(destX-rootX)/Utils.pixelDist(rootX, rootY, destX, destY));
						newY = (int)(rootY + x*30*(destY-rootY)/Utils.pixelDist(rootX, rootY, destX, destY));
						shortDist = x;
						closeUnit = u;
					}
				}
			}
		}
		
		if (closeUnit != null && closeUnit.army != this.army) {
			newX = rootX;
			newY = rootY;
		}
		else if (Utils.inchDist(rootX, rootY, newX, newY) <= this.moveLeft){
			this.x = newX;
			this.y = newY;
			this.moveLeft -= Utils.inchDist(rootX, rootY, newX, newY);
		}
	}
	
	public boolean canContact(Unit u) {
		if (this.army == u.army) {
			return true;
		}
		else if (u.battle == null) {
			return true;
		}
		else if (u.battle.majority == -1 || u.battle.majority == this.army)
			return true;
		return false;
		
	}
	
	public void contact(Unit u) {
		if (this.army != u.army) {
			u.moveLeft = 0;
			this.moveLeft = 0;
			if (u.battle == null) {
				if (u.supporting != null) {
					u.supporting.supporters.remove(u);
					u.supporting = null;
				}
				Battle b = new Battle();
				b.addUnit(this);
				b.addUnit(u);
				Game.battles.add(b);
				u.battle = b;
				this.battle = b;
			}
			else {
				u.battle.addUnit(this);
				this.battle = u.battle;
			}
		}
		else {
			if (this.hasEquipment(new Spear())) {
				this.supporting = u;
				u.supporters.add(this);
			}
			if (u.hasEquipment(new Spear())) {
				u.supporting = this;
				this.supporters.add(u);
			}
		}
	}
	
	public void strike(Unit target) {
		int roll = rng.nextInt(6) + 1;
		System.out.println(this.toChoiceString() + " is attacking " 
				+ target.toChoiceString() + " with roll of " + roll);
		roll += this.woundBonus;
		roll = Math.max(1, Math.min(6,roll));
		target.takeStrike(this.strength, roll);
	}
	
	public void takeStrike(int strength, int roll) {
		if (Utils.wound(roll, strength, this.defense)) {
			this.wounds -= 1;
		}
	}
	
	public void shootAt(Unit u) {
		// Ensure we're allowed to shoot
		if (Utils.inchDist(this, u) > this.shootRange 
				|| this.moveLeft < this.move * this.moveNeededToShoot
				|| this.battle != null
				|| (u.battle != null && this.army == 0)
				|| this.hasShot)
			return;
		
		// This'll need to be changed to deal with obstacles
		System.out.println(this.toString() + " is shooting at " + u.toString());
		this.hasShot = true;
		int hitRoll = rng.nextInt(6)+1;
		System.out.println("Roll to hit: " + hitRoll);
		if (hitRoll < this.shootValue)
			return; // Missed
		Unit target = u;
		if (u.battle != null) {
			int sideRoll = rng.nextInt(2);
			System.out.println("Hit a battle, side hit is: " + sideRoll);
			if (sideRoll == 1) {
				double closestDist = 100;
				for (Unit t : u.battle.badUnits){
					if (Utils.inchDist(this, t) < closestDist) {
						closestDist = Utils.inchDist(this, t);
						target = t;
					}
				}
			}
			System.out.println("Final target is " + target.toString());
		}
		int woundRoll = rng.nextInt(6)+1;
		System.out.println("Roll to wound: " + woundRoll);
		
		target.takeStrike(this.shootStrength, woundRoll);
		if (target.wounds <= 0) {
			Game.armies[target.army].removeUnit(target);
			if (target.battle != null) {
				target.battle.removeUnit(target);
			}
			myUI.game.shootTarget = null;
			// May need to add support for evil units shooting into battles and affecting them here too
		}
		myUI.repaint();
	}
	
	public String toChoiceString() {
		String str = "";
		str += this.name + "-" + this.id;
		return str;
	}
	
	public String equipmentString() {
		String str = "";
		if (this.equipmentList.size() == 0)
			return str;
		int size = equipmentList.get(0).getClass().toString().split("t.").length;
		for (int i = 0; i < this.equipmentList.size()-1; i++) {
			str += equipmentList.get(i).getClass().toString().split("t.")[size-1] + ", ";
		}
		if (this.equipmentList.size() > 0)
			str += equipmentList.get(equipmentList.size()-1).getClass().toString().split("t.")[size-1];
		return str;
	}
	
	public String tabbedStats() {
		String str = "";
		str += this.fightValue + "/" + this.shootValue + "+    ";
		str += this.strength + "    ";
		str += this.defense + "    ";
		str += this.attacks + "     ";
		str += this.wounds + "     ";
		str += this.courage;
		return str;
	}
	
	public static String tabbedHeader() {
		return "F/Sv    S    D    A    W    C";
	}
	
	public boolean equals(Object o) {
		if (!(o instanceof Unit)) return false;
		return ((Unit)o).id == this.id;
		
	}
	
	public int hashCode() {
		return id;
	}
	
	public void refresh(){
		this.moveLeft = this.move;
		this.hasShot = false;
		this.supporting = null;
		this.supporters = new ArrayList<Unit>();
		this.hasRetreated = true;
		for (Equipment e : this.equipmentList){
			if (e.options != null)
				e.options.select(0);
		}
	}
}